package cn.zhoujing.security.demo.common.handler;

import cn.zhoujing.security.demo.common.consts.RedisConst;
import cn.zhoujing.security.demo.common.utils.RedisUtil;
import jakarta.annotation.Resource;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 动态鉴权
 *
 * @author zhoujing
 * @createTime 2024/2/17 - 14:36
 */
public class MyAccessAuthorizationManager implements AuthorizationManager<RequestAuthorizationContext> {


    @Resource
    private RedisUtil redisUtil;

    @Resource
    private AntPathMatcher antPathMatcher;

    /**
     * 动态鉴权
     * @param authentication the {@link Supplier} of the {@link Authentication} to check
     * @param object the {@link T} object to check
     * @return
     */
    @Override
    public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
        // 当前请求的URI
        String requestURI = object.getRequest().getRequestURI();
        // 缓存所有的URI
        Map<Object, Object> uriMap = redisUtil.hmget(RedisConst.PERMISSION_URIS);
        for (Object uriObj : uriMap.keySet()) {
            String uri = uriObj.toString();
            // URI匹配
            if (antPathMatcher.match(uri, requestURI)) {
                String roleCodesStr = (String) uriMap.get(uri);
                if (StringUtils.hasText(roleCodesStr)) {
                    // 访问当前URI所需要的角色
                    Set<String> roleCodes = Arrays.asList(roleCodesStr.split(",")).stream().collect(Collectors.toSet());
                    // 获取当前用户拥有的角色
                    Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();
                    Set<String> curRoles = authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
                    // 角色比较
                    for (String curRole : curRoles) {
                        if (roleCodes.contains(curRole)) {
                            // 通过
                            return new AuthorizationDecision(true);
                        }
                    }
                }
            }
        }
        // 否定
        return new AuthorizationDecision(false);
    }
}
